package com.template.service.impl;

import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.template.common.annotation.CacheRemove;
import com.template.common.lang.Const;
import com.template.domain.entity.SysMenu;
import com.template.domain.vo.MenuVo;
import com.template.mapper.SysMenuMapper;
import com.template.service.SysMenuService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.template.domain.entity.table.SysMenuTableDef.SYS_MENU;

/**
 * 服务层实现。
 *
 * @author dong
 * @since 2023-09-03
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysMenuMapper sysMenuMapper;

    @Override
    public List<MenuVo.Tree> getTree() {
        List<MenuVo.Tree> menuList = this.listAs(this.query(), MenuVo.Tree.class);

        // 构建树状结构
        return findChildNode(menuList, null);
    }

    @Override
    public List<MenuVo.Tree> getAliveTree() {
        List<MenuVo.Tree> menuList = this.listAs(this.query().where(SYS_MENU.STATUS.eq(1)), MenuVo.Tree.class);

        // 构建树状结构
        return findChildNode(menuList, null);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "menu", key = "'alivePerms'"),
            @CacheEvict(value = "menu", key = "'keepalive'")
    })
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public boolean save(SysMenu entity) {
        return super.save(entity);
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "menu", key = "'alivePerms'"),
            @CacheEvict(value = "menu", key = "'keepalive'")
    })
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public boolean updateById(SysMenu entity, boolean ignore) {
        return super.updateById(entity, ignore);
    }

    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "menu", key = "'alivePerms'", condition = "#id != null"),
            @CacheEvict(value = "menu", key = "'keepalive'", condition = "#id != null")
    })
    @CacheRemove({Const.Cache.AUTHORITY + "*"})
    public void removeCascadeById(Long id) {
        if (id == null) return;
        Set<Long> ids = new HashSet<>() {{
            add(id);
        }};
        Set<Long> childrenIds = getChildrenRecurrence(ids);
        this.removeByIds(childrenIds);
    }

    @Override
    @Cacheable(value = "menu", key = "'alivePerms'")
    public List<String> getAliveMenuPerms() {
        return this.queryChain()
                .select(SYS_MENU.PERMS)
                .where(SYS_MENU.STATUS.eq(true))
                .and(SYS_MENU.PERMS.isNotNull())
                .list()
                .stream().map(SysMenu::getPerms).collect(Collectors.toCollection(ArrayList::new));
    }

    @Override
    @Cacheable(value = "menu", key = "'keepalive'")
    public List<String> getKeepaliveMenu() {
        List<SysMenu> list = this.queryChain()
                .select(SYS_MENU.COMPONENT)
                .where(SYS_MENU.TYPE.eq(SysMenu.Type.Page))
                .and(SYS_MENU.CACHE.eq(true))
                .list();
        return list.stream().map(SysMenu::getComponent).collect(Collectors.toCollection(ArrayList::new));
    }

    private Set<Long> getChildrenRecurrence(Set<Long> parentIds) {
        if (parentIds == null || parentIds.size() == 0) return new HashSet<>();
        List<SysMenu> children = this.queryChain().select(SYS_MENU.ID).where(SYS_MENU.PARENT_ID.in(parentIds)).list();
        Set<Long> childrenIds = children.stream().map(SysMenu::getId).collect(Collectors.toSet());
        Set<Long> childrenRecurrence = getChildrenRecurrence(childrenIds);
        parentIds.addAll(childrenRecurrence);
        return parentIds;
    }


    /**
     * 递归获取获取菜单子节点
     *
     * @param list
     * @param node
     * @return
     */
    private static List<MenuVo.Tree> findChildNode(List<MenuVo.Tree> list, MenuVo.Tree node) {
        return list.stream()
                .filter(menu -> {
                    // 若node为null，则返回所有根节点
                    if (node == null) return menu.getParentId() == null;
                    // 否则返回node的子节点
                    return Objects.equals(menu.getParentId(), node.getId());
                })
                .sorted(SysMenu::compare)
                .peek(menu -> menu.setChildren(findChildNode(list, menu)))
                .toList();
    }

}
